/**
 *	The NeuroCoSA Toolkit
 *	Copyright (C) 2003-6 Stuart Meikle.
 *
 *	This is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation; either
 *	version 2.1 of the License, or (at your option) any later version.
 *
 *	This library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *	Lesser General Public License for more details.
 *
 * @author	Stuart Meikle
 * @version	2006-halloween(mk2)
 * @license	LGPL
 */
package org.stumeikle.NeuroCoSA;
import java.util.*;
import org.stumeikle.NeuroCoSA.PubSub.Subscriber;
import org.stumeikle.NeuroCoSA.PubSub.Publication;
import org.stumeikle.NeuroCoSA.NIS.*;

//complete re-write i think today. 


/**
 * The Cortex. 
 * Cortexs are now collections of layers. Cortexs are DPUs. They have names and information services. 
 * Their inputs and outputs are co-ordinated by the brain. They need to know about the update sequence so
 * that they can return the correct information to the neurons and layers that request it. 
 * Each lobe will have a relative drive strength which indicates the relative strength of the drives coming
 * from it. Cortexs also need to know which of the layers is the most fundemental. This is the one that
 * incoming drive connections should bond to. Links to each of the layer information services are
 * needed
 * 
 * @author	Stuart Meikle
 * @version	2006-halloween(mk2)
 *
 */
public class	Cortex extends DPU implements Subscriber//lobe takes datablock and returns datablock
{  
    CortexInfoService		iCIS;
    Vector<Layer>		iLayers;
    Layer			iLowestLayer;
    Publication[]		iInputCopies;
    Brain			iParent;

//WE NEED TO KNOW WHICH LAYER IS THE BASE LAYER.
//(This is the one which other lobes will bind to).
//So make this have a hierarchy level of its own

    // to come BrainDEBUG			iBrainDEBUG;

    //-----------------------------------------------------------------------------------------------
    public Cortex(Brain b)
    {
	iParent = b;
	iCIS = new CortexInfoService(iParent.getInfoService());

	//subscribe to the name entry
	iCIS.findInfoString("CortexName").subscribe(this);

	//iCortexDEBUG=null;
	iLayers = new Vector<Layer>();
	iInputCopies=null;
	iLowestLayer = null;
    }
    
    //-----------------------------------------------------------------------------------------------
    public CortexInfoService	getInfoService()
    { return iCIS; }

    //public void			enableGraphicalDebug()
    //{ iBrainDEBUG = new BrainDEBUG(this); }

    public Vector<Layer>	getLayers()
    { return iLayers; }

    public void			addLayer( Layer c )
    { addLayer(c,1); }

    public void			addLayer( Layer c, int hierarchylevel )
    {
	//add the layer in
	c.setHierarchyLevel( hierarchylevel );
    	iLayers.add(c);
    }

    //FUTURE: put the number of layers in the CInfo Service
    //plus ptr to list of layers or layer names

    //add in: name setting. update , time update, initialisation etc
    public void			getReady()
    {
	//sort the layers based on hierarchy level
	//store the update sequence info in the layers
	Collections.sort( iLayers );
	//Collections.reverse( iLayers ); removed 20071209 stumeikle. why was this here ?
	//we need higher order layers updated after lower order layers. 

	//store the update order in each layer. 
	int 	count=1;
	ListIterator<Layer>	i = iLayers.listIterator();
	for(;i.hasNext();)
	{
	    Layer	c= (Layer)i.next();
	
	    c.setUpdateOrder(count);
	    if (count==1)	iLowestLayer = c;
	    count ++;
	}

	//store the number of layers in the info service
	iCIS.findInfoLong("NumberOfLayers").setValue(count-1);

	//store the lowest layer's information service in the CIS
	//this is the layer that other cortices should bind to
	if (iLowestLayer!=null)
	    iCIS.findInfo("BindLayerInfoService").setValue( iLowestLayer.getInfoService() );

	//order the layers if necessary
	//setRelativeDriveStrength( 5.0 );
   	//    setInputs( xxx );
	//    setOutputs( xxx );
	//    getPositionInUpdateSequence();

	//set all the defaults. 

	//at this point we know the number of inputs, so we can set up the LIS 
	//accordingly.
	ListIterator<DPU>	j=getInputs().listIterator();
	count=0;
	
	iInputCopies = new Publication[getInputs().size()];
	for(;j.hasNext();)
	{
	    DPU		d = (DPU)j.next();
	    Info	n = new Info( "Input" + count );
	    
	    //Store DATABLOCKS in the LIS
	    iCIS.addPublication( n );
	    //(we don't need to store the actual DPU outputs becuase they should be empty 
	    // at this point)
	    
	    iInputCopies[count]=n;
	    count++;
	}	

	//trigger the layers to get themselves ready
	i = iLayers.listIterator();
	for(;i.hasNext();)
	{
	    Layer	c= (Layer)i.next();
	
	    c.getReady();
	}
    }

    final public	void		update()
    {
	//Called by the DPUMachine

	//il mano padro
	//inputs and outputs are already defined

	//update all the layers 
	
	//call the brainupdate method peut etre, depending whether the lobe is shutdown or not
	//if the lobe is to be updated, first copy the inputs into the LIS. how to do this ? 
	//

	//(1) copy the inputs into the LIS
	ListIterator<DPU>	i = getInputs().listIterator();
	//ASSUME that the order remains the same :-). might do eh?
	int			count=0;
	for(;i.hasNext();)
	{
	    DPU	d = (DPU)i.next();

	    if (d.getOutput()!=null)
	    {
		Publication	p = iInputCopies[count];
	
		p.setValue( d.getOutput() );
	    }

	    count++;
	}	

	cortexUpdate();
    }

    final public void			cortexUpdate()
    {
	
	/* --> not sure this is really the case now. generally lobes will now update the layers in
	 * order. Layers themselves will have to overload the update mech
	System.out.println("ERROR : lobeUpdate function should be overloaded by each specific lobe");
	System.exit(1);
	*/

	ListIterator<Layer>	i = getLayers().listIterator();
	for(;i.hasNext();)
	{
	    Layer	c = (Layer)i.next();
	    c.cortexUpdate();
	}
    }

    public	void			notify( long iID, Object iValue, long iVersion )
    {
	//dont need to check the id yet
	//set the name in the DPU base class to match the name in the infoservice
	setName( (String) iValue );
    }

    public	LayerInfoService	findBindLayerInfoServiceFromCortex(String n)
    {
	CortexInfoService	cis;

	cis = iParent.findNamedCortexInfoService( n );
	return (LayerInfoService) cis.findInfo("BindLayerInfoService").getValue();
    }

    public	Layer			getLayerByName(String n)
    {
	//return a pointer to a named layer. used by layers within this cortex. should
	//not be used by layers outside of this particular cortex. 

	ListIterator<Layer>	i = getLayers().listIterator();
	for(;i.hasNext();)
	{
	    Layer	c = (Layer)i.next();
	
	    String	layer_name = (String) c.getInfoService().findInfoString("LayerName").getValue();
	    if (layer_name.compareTo( n ) ==0)
	    {
		return c;
	    }
	}
	return null;
    }


//-------------------stuff to add -------------------------------------

/*public	void		disableShutdown()
    { iOKToShutdown = false; }
*/
}


